package cn.zhangfusheng.elasticsearch.factory;

import cn.zhangfusheng.elasticsearch.cycle.InitTransferBefore;
import cn.zhangfusheng.elasticsearch.factory.proxy.RepositoryCglibProxy;
import cn.zhangfusheng.elasticsearch.factory.proxy.RepositoryJdkProxy;
import cn.zhangfusheng.elasticsearch.factory.proxy.RepositoryProxy;
import cn.zhangfusheng.elasticsearch.mybatis.ElasticSearchWithMybatisXmlConfig;
import cn.zhangfusheng.elasticsearch.repository.DefaultElasticSearchRepository;
import cn.zhangfusheng.elasticsearch.repository.ElasticSearchRepository;
import cn.zhangfusheng.elasticsearch.scan.ElasticSearchEntityRepositoryDetail;
import cn.zhangfusheng.elasticsearch.template.ElasticSearchRestTemplate;
import cn.zhangfusheng.elasticsearch.transfer.TransferInfo;
import cn.zhangfusheng.elasticsearch.transfer.TransferOperation;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.AdviceMode;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.util.Map;
import java.util.Objects;

/**
 * @author fusheng.zhang
 * @date 2022-02-24 13:38:44
 */
@Data
@Slf4j
public class ElasticSearchRepositoryFactoryBean<T extends ElasticSearchRepository<?>> implements FactoryBean<T>, InitializingBean {

    private ElasticSearchEntityRepositoryDetail entityRepositoryDetail;
    private DefaultListableBeanFactory beanFactory;
    private ElasticSearchWithMybatisXmlConfig elasticSearchWithMybatisXmlConfig;
    private Class<? extends ElasticSearchRepository<?>> objectType;
    private AdviceMode adviceMode;

    private ElasticSearchRepository<?> defaultElasticSearchRepository;
    private ElasticSearchRestTemplate elasticSearchRestTemplate;

    private String indexName;

    private T object;

    @Override
    public T getObject() {
        if (Objects.isNull(object)) {
            RepositoryProxy<T> repositoryCglibProxy = adviceMode.equals(AdviceMode.PROXY)
                    ? new RepositoryJdkProxy<>(defaultElasticSearchRepository, entityRepositoryDetail, elasticSearchRestTemplate, elasticSearchWithMybatisXmlConfig)
                    : new RepositoryCglibProxy<>(defaultElasticSearchRepository, entityRepositoryDetail, elasticSearchRestTemplate, elasticSearchWithMybatisXmlConfig);
            this.object = repositoryCglibProxy.getObject();
        }
        return object;
    }

    @Override
    public Class<?> getObjectType() {
        return this.objectType;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.beanFactory, "beanFactory must not be null");
        Assert.notNull(this.entityRepositoryDetail, "entityRepositoryDetail must not be null");
        this.elasticSearchRestTemplate = beanFactory.getBean(ElasticSearchRestTemplate.class);
        Assert.notNull(this.elasticSearchRestTemplate, "entityRepositoryDetail must not be null");
        this.defaultElasticSearchRepository =
                new DefaultElasticSearchRepository<>(this.elasticSearchRestTemplate, this.entityRepositoryDetail);
        //
        this.indexName = this.entityRepositoryDetail.getIndexName();
        // create Transfer index
        if (Objects.equals(entityRepositoryDetail.getEntityClass(), TransferInfo.class)) {
            this.initTransfer();
        } else if (!entityRepositoryDetail.isIgnoreTransferOperation()) {
            // 处理mapping和数据迁移
            new TransferOperation(entityRepositoryDetail, elasticSearchRestTemplate).operationDocumentAndTransferDb();
        }
    }

    /**
     * 初始化版本迁移的索引
     */
    private void initTransfer() {
        // 数据迁移控制
        boolean indexExists = this.elasticSearchRestTemplate.exists(new GetIndexRequest(indexName));
        if (!indexExists) {
            // 执行创建mapping前的方法
            Map<String, InitTransferBefore> initTransferBeforeMap = beanFactory.getBeansOfType(InitTransferBefore.class);
            if (!CollectionUtils.isEmpty(initTransferBeforeMap)) {
                initTransferBeforeMap.values().forEach(r -> r.run(elasticSearchRestTemplate));
            }
            elasticSearchRestTemplate.createIndexMapping(indexName, entityRepositoryDetail.getMapping(), null);
            elasticSearchRestTemplate.indexSetAlias(indexName, entityRepositoryDetail.getAlias());
            if (log.isDebugEnabled()) log.debug("create transfer index success");
        } else {
            if (log.isDebugEnabled()) log.debug("transfer index exists:{}", indexExists);
        }
    }
}
